package com.codef.sharp.gateway.filter;

import com.codef.sharp.common.cache.constant.CacheConstant;
import com.codef.sharp.common.core.enums.ResponseStatus;
import com.codef.sharp.common.core.response.ResponseEntity;
import com.codef.sharp.gateway.props.AuthProperties;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.xiaoymin.knife4j.core.util.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

/**
 * 网关鉴权
 * @author zhanrf
 * @date 2022/1/26 09:15
 */
@Component
@ConditionalOnProperty(value = "gateway.auth.enable", havingValue = "true")
public class AuthFilter implements GlobalFilter, Ordered {
    private static final Logger log = LoggerFactory.getLogger(AuthFilter.class);

    @Autowired
    private AuthProperties authProperties;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        // 放行白名单
        String path = request.getURI().getPath();
        if (match(path)) {
            return chain.filter(exchange);
        }
        String token = request.getHeaders().getFirst(CacheConstant.HEADER);
        // 没有令牌，鉴权失败
        if (StrUtil.isBlank(token)) {
            return unAuth(exchange.getResponse(), ResponseStatus.UNAUTHORIZED);
        }
        // 令牌过期
//        stringRedisTemplate.opsForValue().get()
        if (true) {
            return unAuth(exchange.getResponse(), ResponseStatus.TOKEN_EXPIRE);
        }
        return chain.filter(exchange);
    }

    /**
     * 匹配白名单
     * @param path 需要匹配的路径
     */
    private boolean match(String path) {
        for (String white : authProperties.getWhites()) {
            if (white.contains(path)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 认证失败
     * @param resp 响应体
     * @param responseStatus 响应状态
     */
    private Mono<Void> unAuth(ServerHttpResponse resp, ResponseStatus responseStatus) {
        resp.setStatusCode(HttpStatus.UNAUTHORIZED);
        resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        String result = "";
        try {
            result = objectMapper.writeValueAsString(ResponseEntity.error(responseStatus.value(), responseStatus.getMsg()));
        } catch (JsonProcessingException e) {
            log.error(e.getMessage(), e);
        }
        DataBuffer buffer = resp.bufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8));
        return resp.writeWith(Flux.just(buffer));
    }

    @Override
    public int getOrder() {
        return -200;
    }
}
